Shader "RedSaw/VolumetricCloud"{
    
    Properties{
        _BaseColor("Base Color", Color) = (1, 1, 1, 1)
    }
    SubShader{
        Tags{
            "RenderPipeline" = "UniversalRenderPipeline"
        }
        pass{

            Cull Off
            ZTest Always
            ZWrite Off
            
            HLSLPROGRAM

                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
                #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
                #pragma vertex Vertex
                #pragma fragment Pixel

                Texture2D _SourceTex;
                SamplerState sampler_SourceTex;
                half4 _BaseColor;
                float3 _CloudMaxDis;
                float3 _CloudMinDis;

                
                float3 GetWorldPosition(float3 positionHCS)
                {
                    /* get world space position from clip position */

                    float2 UV = positionHCS.xy / _ScaledScreenParams.xy;
                    #if UNITY_REVERSED_Z
                    real depth = SampleSceneDepth(UV);
                    #else
                    real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
                    #endif
                    return ComputeWorldSpacePosition(UV, depth, UNITY_MATRIX_I_VP);
                }
                float2 rayBoxDst(float3 boundsMin, float3 boundsMax, float3 rayOrigin, float3 rayDir)
                {
                    /*  通过boundsMin和boundsMax锚定一个长方体包围盒
                        从rayOrigin朝rayDir发射一条射线，计算出射线到包围盒的距离
                        about ray box algorithm 
                        https://jcgt.org/published/0007/03/04/ */

                    float3 t0 = (boundsMin - rayOrigin) / rayDir;
                    float3 t1 = (boundsMax - rayOrigin) / rayDir;
                    float3 tmin = min(t0, t1);
                    float3 tmax = max(t0, t1);

                    float dstA = max(max(tmin.x, tmin.y), tmin.z);
                    float dstB = min(tmax.x, min(tmax.y, tmax.z));

                    float dstToBox = max(0, dstA);
                    float dstInsideBox = max(0, dstB - dstToBox);
                    return float2(dstToBox, dstInsideBox);
                }
                struct vertexInput{
                    float4 vertex: POSITION;
                    float2 uv: TEXCOORD0;
                };
                struct vertexOutput{
                    float4 pos: SV_POSITION;
                    float2 uv: TEXCOORD0;
                };

                vertexOutput Vertex(vertexInput v){

                    vertexOutput o;
                    o.pos = TransformObjectToHClip(v.vertex);
                    o.uv = v.uv;
                    return o;
                }
                half4 Pixel(vertexOutput IN): SV_TARGET
                {
                     // 采样主纹理
                    half4 albedo = _SourceTex.Sample(sampler_SourceTex, IN.uv);

                    // 重建世界坐标
                    float3 worldPosition = GetWorldPosition(IN.pos);
                    float3 rayPosition = _WorldSpaceCameraPos.xyz;
                    float3 worldViewVector = worldPosition - rayPosition;
                    float3 rayDir = normalize(worldViewVector);


                    // 碰撞体积计算
                    float2 rayBoxInfo = rayBoxDst(
                        _CloudMinDis, _CloudMaxDis,
                        rayPosition, rayDir);
                    float dstToBox = rayBoxInfo.x;
                    float dstInsideBox = rayBoxInfo.y;
                    float dstToOpaque = length(worldViewVector);
                    float dstLimit = min(dstToOpaque - dstToBox, dstInsideBox);

                    int stepCount = 32;                                     // 总步数
                    float3 entryPoint = rayPosition + rayDir * dstToBox;    // 采样起点
                    float stepSize = dstInsideBox / stepCount;              // 步长
                    float totalDensity = 0;                                 // 浓度积分
                    float dstTravelled = 0;					// 已经走过的距离，与dstLimit所比较可以中断当前的采样



                    for(int i = 0; i < stepCount; i ++)
                    {
                        if(dstTravelled < dstLimit)
                        {
                            totalDensity += stepSize * 0.1;
                        }
                        else
                        {
                            break;
                        }
                        dstTravelled += stepSize;
                    }
                    return albedo + totalDensity;
                }
            ENDHLSL
        }
    }
}